﻿using System;
using System.IO;
using System.Threading;

using Microsoft.Coding4Fun.TweetCraft.Wow;

using NUnit.Framework;

namespace Microsoft.Coding4Fun.TweetCraft.Tests
{
    [TestFixture]
    public class SavedVariablesChannelTest
    {
        private readonly string savedVariablesPath = Path.GetFullPath("SavedVariablesChannelTest.lua");

        private ISavedVariablesChannel savedVariablesChannel;

        [SetUp]
        public void SetUp()
        {
            // Delete the existing SavedVariables file
            if (File.Exists(savedVariablesPath))
                File.Delete(savedVariablesPath);

            // Initialize the SavedVariables channel with the path to the SavedVariables file
            savedVariablesChannel = new SavedVariablesChannel(savedVariablesPath);
        }

        [TearDown]
        public void TearDown()
        {
            if (savedVariablesChannel != null)
                ((IDisposable)savedVariablesChannel).Dispose();
        }

        [Test]
        public void ReadTest()
        {
            // Create a SavedVariables file to be read from
            File.WriteAllText(savedVariablesPath, "testValue = 42");

            // Reinitialize SavedVariables channel after file has been created
            savedVariablesChannel = new SavedVariablesChannel(savedVariablesPath);

            // Read the test value out of the channel
            int result = savedVariablesChannel.ReadValue<int>("testValue");

            // Assert that the testValue is equal to the expected value
            Assert.AreEqual(42, result);
        }

        [Test]
        public void FlushTest()
        {
            const int testValue = 42;

            // Queue values into the channel
            savedVariablesChannel.QueueValue("testValue", testValue);

            // Perform an explicit Flush on the channel
            savedVariablesChannel.Flush();

            // Assert that the SavedVariables file has been created
            Assert.IsTrue(File.Exists(savedVariablesPath));
        }

        [Test]
        public void ChannelUpdatedTest()
        {
            // Assert that the test value is not available in the channel
            Assert.IsFalse(savedVariablesChannel.IsAvailable("testValue"));

            // Create an auto-reset event to signal the expected ChannelUpdated event
            var channelUpdatedEvent = new AutoResetEvent(false);

            // Subscribe to ChannelUpdated event and assert channel content
            savedVariablesChannel.ChannelUpdated += delegate
                                                    {
                                                        // Assert that testValue is available
                                                        Assert.IsTrue(savedVariablesChannel.IsAvailable("testValue"));

                                                        // Read the test value out of the channel
                                                        int result = savedVariablesChannel.ReadValue<int>("testValue");

                                                        // Assert that the testValue is equal to the expected value
                                                        Assert.AreEqual(42, result);

                                                        // Signal that the ChannelUpdated event was raised
                                                        channelUpdatedEvent.Set();
                                                    };

            // Update the SavedVariables file
            File.WriteAllText(savedVariablesPath, "testValue = 42");

            // Wait for the ChannelUpdated event with timeout
            if (!channelUpdatedEvent.WaitOne(TimeSpan.FromSeconds(10)))
            {
                // Fail the test as we timed out
                Assert.Fail("Timed out while waiting for ChannelUpdated event.");
            }
        }

        [Test]
        public void FlushedTest()
        {
            // Create an auto-reset event to signal the expected Flushed event
            var flushedEvent = new AutoResetEvent(false);

            // Subscribe to Flushed event and signal when raised
            savedVariablesChannel.Flushed += delegate
                                             {
                                                 flushedEvent.Set();
                                             };

            // Perform flush
            savedVariablesChannel.Flush();

            // Wait for the ChannelUpdated event with timeout
            if (!flushedEvent.WaitOne(TimeSpan.FromSeconds(1)))
            {
                // Fail the test as we timed out
                Assert.Fail("Timed out while waiting for Flushed event.");
            }
        }

        [Test]
        public void AutoFlushTest()
        {
            // Create an auto-reset event to signal the expected ChannelUpdated and Flushed events
            var channelUpdatedEvent = new AutoResetEvent(false);
            var flushedEvent = new AutoResetEvent(false);

            // Enable auto-flushing of queued values after the channel has been updated
            savedVariablesChannel.AutoFlush = true;

            // Subscribe to ChannelUpdated event and assert channel content
            savedVariablesChannel.ChannelUpdated += delegate
                                                    {
                                                        // Assert that testValue is available
                                                        Assert.IsTrue(savedVariablesChannel.IsAvailable("testValue"));

                                                        // Read the test value out of the channel
                                                        int result = savedVariablesChannel.ReadValue<int>("testValue");

                                                        // Assert that the testValue is equal to the expected value
                                                        Assert.AreEqual(42, result);

                                                        // Signal that the ChannelUpdated event was raised
                                                        channelUpdatedEvent.Set();
                                                    };

            // Subscribe to Flushed event and signal
            savedVariablesChannel.Flushed += delegate
                                             {
                                                 // Assert that testValue is available
                                                 Assert.IsTrue(savedVariablesChannel.IsAvailable("testValue"));

                                                 // Read the test value out of the channel
                                                 int result = savedVariablesChannel.ReadValue<int>("testValue");

                                                 // Assert that the testValue is equal to the expected value
                                                 Assert.AreEqual(69, result);

                                                 // Signal that the ChannelUpdated event was raised
                                                 flushedEvent.Set();
                                             };

            // Queue value into the channel
            savedVariablesChannel.QueueValue("testValue", 69);

            // Update the SavedVariables file
            File.WriteAllText(savedVariablesPath, "testValue = 42");

            // Wait for the ChannelUpdated event with timeout
            if (!channelUpdatedEvent.WaitOne(TimeSpan.FromSeconds(10)))
            {
                // Fail the test as we timed out
                Assert.Fail("Timed out while waiting for ChannelUpdated event.");
            }

            // Wait for the Flushed event with timeout
            if (!flushedEvent.WaitOne(TimeSpan.FromSeconds(10)))
            {
                // Fail the test as we timed out
                Assert.Fail("Timed out while waiting for Flushed event.");
            }
        }

        [Test]
        public void KeepValueTest()
        {
            // Create a SavedVariables file to be read from
            File.WriteAllText(savedVariablesPath, "testValue = 42");

            // Reinitialize SavedVariables channel after file has been created
            savedVariablesChannel = new SavedVariablesChannel(savedVariablesPath);

            // Keep the testValue in the channel
            savedVariablesChannel.KeepValue<int>("testValue");

            // Flush the channel
            savedVariablesChannel.Flush();

            // Assert that the value is still available
            savedVariablesChannel.IsAvailable("testValue");
        }
    }
}
